import * as ObsUtil from './obsUtil';
import * as MinioUtil from './minioUtil';
import { useUploadFileStore } from '/@/store/modules/uploadFile';
import type { AppRouteRecordRaw } from '/@/router/types';
import { useFileDialog } from '/@/hooks/web/useFileModal';
import { buildUUID } from './uuid';
import { useUserStore } from '/@/store/modules/user';
import { useMessage } from '/@/hooks/web/useMessage';
import { getObsDownloadUrl } from '/@/api/process/process';
import { uploadChunk, registerFile, mergeFile, delFile } from '/@/api/pro/pro';
import SparkMD5 from 'spark-md5';
import { cloneDeep } from 'lodash-es';

const uploadFileStore = useUploadFileStore();
const userStore = useUserStore();
const { createMessage } = useMessage();

const fileObj: any = () => {
  if (userStore.getServerInfo.objStoreType == 'obs') {
    return ObsUtil;
  } else if (userStore.getServerInfo.objStoreType == 'minio') {
    return MinioUtil;
  }
};

export enum UploadStatus {
  READY = 0,
  PAUSE = 1,
  START = 2,
  CANCEL = 3,
  SUCCESS = 4,
}

export interface UploadFileParams {
  formName: string | undefined; //表单名
  fieldValue: string; //上传成功后文件名称
  fileName: string; //文件名
  progress: number; //进度
  file: any;
  key: string;
  title: string;
  fullPath: string | undefined; // 路由，匹配选项卡及高亮突出显示当前选项卡的名称
  filePath: string | undefined; // 上传至obs的路径
  fieldName: string | undefined;
  formCode: string | undefined;
  pageType: string | undefined;
  mainId: number;
  fieldTitle: string;
  macroType: string;
  status: UploadStatus;
  myActionId: number;
}

// 单文件 Or 多文件
export class uploadFile {
  public formName: string | undefined;
  public fieldValue: string;
  public fileName: string;
  public progress: number;
  public key: string;
  public title: string;
  public file: any;
  public level: number;
  public fullPath: string | undefined;
  public filePath: string | undefined;
  public fieldName: string | undefined;
  public formCode: string | undefined;
  public pageType: string | undefined;
  public mainId: number | undefined;
  public fieldTitle: string | undefined;
  public macroType: string | undefined;
  public status: UploadStatus;
  public myActionId: number | undefined;

  constructor(opt: Partial<UploadFileParams> = {}) {
    const {
      formName,
      file,
      fullPath,
      filePath,
      fieldName,
      formCode,
      pageType,
      mainId,
      fieldTitle,
      macroType,
      myActionId,
    } = opt;
    this.formName = formName;
    this.file = file;
    this.fieldValue = '';
    this.fileName = file?.name;
    this.title = file?.name;
    this.key = file?.name;
    this.level = 2;
    this.fullPath = fullPath;
    this.progress = 0;
    this.filePath = filePath;
    this.fieldName = fieldName;
    this.formCode = formCode;
    this.pageType = pageType;
    this.mainId = mainId;
    this.fieldTitle = fieldTitle;
    this.macroType = macroType;
    this.status = UploadStatus.READY;
    this.myActionId = myActionId;

    const serverInfo = userStore.getServerInfo;
    if (serverInfo.isUseBreakpointResume) {
      // 断点续传
      const data = cloneDeep(opt);
      // 删除参数中的原文件，以免在上传分片时带上了整个文件
      delete data.file;
      const option = { file: opt.file, data: data };
      upload({ ...option }, this);
    } else {
      this.putObject();
    }
  }

  // 暂停后启动
  public startUpload() {
    const options = this.getOptions();
    const option = { file: options.file, data: options };
    // 删除参数中的原文件，以免在上传分片时带上了整个文件
    delete options.file;
    upload({ ...option }, this);
  }

  public pauseUpload() {
    this.status = UploadStatus.PAUSE;
  }

  public cancelUpload() {
    const options = this.getOptions();
    this.status = UploadStatus.CANCEL;
    delFile({ fileName: options.file.name });
  }

  public getOptions() {
    return {
      formName: this.formName,
      fieldValue: this.fieldValue,
      fileName: this.fileName,
      progress: this.progress,
      level: this.level,
      file: this.file,
      title: this.title,
      fieldName: this.fieldName,
      formCode: this.formCode,
      pageType: this.pageType,
      mainId: this.mainId,
      filePath: this.filePath,
      fieldTitle: this.fieldTitle,
      macroType: this.macroType,
      myActionId: this.myActionId,
    };
  }

  public putObject = async () => {
    if (this.file) {
      this.fieldValue =
        `${this.filePath}/${buildUUID(20)}.` +
        this.file.name.substring(this.file.name.lastIndexOf('.') + 1);
      console.log('fileObj', fileObj());
      await fileObj().putObject(
        this.file,
        this.fieldValue,
        this.setProgress,
        this.putObjectCallback,
      );
    }
  };

  public putObjectCallback = (res) => {
    // 上传结束回调
    console.log('上传结束', res);
    const newWindow = (window as any) || undefined;
    const data = this.getOptions();
    console.log('putObjectCallback', data);
    if (newWindow && typeof newWindow?.putUploadFinish == 'function') {
      newWindow.putUploadFinish(data);
    }
  };

  private setProgress = (
    transferredAmount: number,
    totalAmount: number,
    totalSeconds: number,
  ): void => {
    console.log('callback', { transferredAmount, totalAmount, totalSeconds });
    // 获取上传平均速率（KB/S）
    console.log('平均速率（KB/S）', (transferredAmount * 1.0) / totalSeconds / 1024);
    // 获取上传进度百分比
    console.log('进度百分比', (transferredAmount * 100.0) / totalAmount);
    const prog = parseInt((transferredAmount * 100.0) / totalAmount);

    if (prog == this.progress) return;

    this.progress = prog;
    const data = this.getOptions();
    finishRefresh(data);
  };
}

export const finishRefresh = (data) => {
  console.log('结束时调用', data);
  // 刷新uploadFileTreeList
  uploadFileStore.setUploadFileTreeList(uploadFileStore.getUploadFileTreeList);

  const serverInfo = userStore.getServerInfo;
  if (serverInfo.isUseBreakpointResume) {
    data.isUseBreakpointResume = serverInfo.isUseBreakpointResume;
  }

  // 刷新文件宏控件的进度条
  const newWindow = (window as any) || undefined;
  if (newWindow && typeof newWindow?.putUploadProgress == 'function') {
    newWindow.putUploadProgress(data);
  } else {
    console.error('finishRefresh: function putUploadProgress is not found');
  }
};

export interface UploadFileObjectParams {
  files: File[] | undefined;
  formName: string;
  route: AppRouteRecordRaw;
  multiple?: boolean;
  fieldName: string;
  filePath: string;
  formCode: string;
  pageType: string;
  mainId: number;
  fieldTitle: string;
  macroType: string;
  myActionId: number;
}

export const uploadFileObject = (opt: Partial<UploadFileObjectParams> = {}) => {
  const {
    files,
    formName,
    route,
    fieldName,
    filePath,
    formCode,
    pageType,
    mainId,
    fieldTitle,
    macroType,
    myActionId,
  } = opt;
  console.log('上传文件传入参数', { files, formName, route, fieldName, filePath });

  // uploadFileStore
  if (!files) {
    return;
  }

  const serverInfo = userStore.getServerInfo;
  if (serverInfo.isUploadPanel) {
    // 显示悬浮进度栏
    uploadFileStore.setIsSelectFileAfter(true);
  }

  //multiple  false is one, true is more
  for (let i = 0; i < files.length; i++) {
    const file = files[i];
    const obj = new uploadFile({
      formName,
      file,
      fullPath: route?.fullPath,
      fieldName,
      filePath,
      formCode,
      pageType,
      mainId,
      fieldTitle,
      macroType,
      myActionId,
    });
    const fileTreeList = uploadFileStore.getUploadFileTreeList;
    const findIndex = fileTreeList.findIndex(
      (item) => item.title === (route?.query?.titleName || route?.meta?.title),
    );
    console.log('findIndex', findIndex);
    // 如果是断点续传
    if (serverInfo.isUseBreakpointResume && findIndex != -1) {
      // 如果原来的obj已存在，则需清除，否则断点续时取消上传，重复上传时，会取到之前的obj
      fileTreeList[findIndex].children = fileTreeList[findIndex].children.filter(
        (item) => item.fieldName != fieldName && item.formName != formName,
      );
      fileTreeList[findIndex].children.push(obj);
    } else {
      const newObj = {
        title: route?.query?.titleName || route?.meta?.title,
        key: route?.query?.titleName || route?.meta?.title,
        path: route?.path,
        level: 1,
        fullPath: route?.fullPath,
        children: [obj],
      };
      fileTreeList.push(newObj);
    }
    console.log('fileTreeList', fileTreeList);
    uploadFileStore.setUploadFileTreeList(fileTreeList);
  }
};

export const isUploadFinished = (tabName) => {
  const fileTreeList = uploadFileStore.getUploadFileTreeList;
  const findIndex = fileTreeList.findIndex((item) => item.title === tabName);
  console.log('findIndex', findIndex);
  if (findIndex != -1) {
    const hasNotFinished = fileTreeList[findIndex].children.some((item) => item.progress < 100);
    return !hasNotFinished;
  } else {
    console.warn(tabName + ' is not exit in fileTreeList');
    return true;
  }
};

// 删除UploadFileTreeList中表单域fieldName对应的节点和进度条
export const deleteByField = (fieldName, key) => {
  let treeList = uploadFileStore.getUploadFileTreeList;
  console.log('deleteByField treeList', treeList, 'fieldName', fieldName);
  treeList.forEach((item) => {
    item.children = item.children.filter((item) => item.fieldName != fieldName);
  });
  treeList = treeList.filter((item) => item.children.length > 0);
  // 当在模块添加页面删除时，需同步删除obs上的文件
  if (key) {
    fileObj().deleteObject(key, (result) => {});
  }
  uploadFileStore.setUploadFileTreeList(treeList);
};

export const deleteByUid = (uid) => {
  let treeList = uploadFileStore.getUploadFileTreeList;
  console.log('deleteByUid treeList', treeList, 'uid', uid);
  treeList.forEach((item) => {
    item.children = item.children.filter((item) => item.file.uid && item.file.uid != uid);
  });
  treeList = treeList.filter((item) => item.children.length > 0);
  uploadFileStore.setUploadFileTreeList(treeList);
};

// 暂停
export const doPause = (formName, fieldName) => {
  const curUploadObj = getCurUploadObj(formName, fieldName);
  if (curUploadObj) {
    curUploadObj.pauseUpload();
  }
  showPauseBtn(formName, fieldName, false);
  showStartBtn(formName, fieldName, true);
  showCancelBtn(formName, fieldName, true);
};

// 暂停后启动
export const doStart = (formName, fieldName) => {
  const curUploadObj = getCurUploadObj(formName, fieldName);
  if (curUploadObj) {
    curUploadObj.status = UploadStatus.START;
    curUploadObj.startUpload();
  }
  showPauseBtn(formName, fieldName, true);
  showStartBtn(formName, fieldName, false);
  showCancelBtn(formName, fieldName, true);
};

export const doCancel = async (formName, fieldName) => {
  const curUploadObj = getCurUploadObj(formName, fieldName);
  if (curUploadObj) {
    curUploadObj.cancelUpload();
    showPauseBtn(formName, fieldName, false);
    showStartBtn(formName, fieldName, false);
    showCancelBtn(formName, fieldName, false);
  }
};

export interface SelectFilesParams {
  formName: string;
  fieldName: string;
  route: AppRouteRecordRaw;
  multiple: boolean;
  filePath: string;
  formCode: string;
  pageType: string;
  mainId: number;
  fieldTitle: string;
  accept: string;
  validExt: string[];
  maxFileSize: number;
  macroType: string;
  myActionId: number;
}

//page import this method
export const selectFiles = (opt: Partial<SelectFilesParams> = {}, callback: (files) => {}) => {
  // uploadFileStore.setUploadFileParams(opt);
  const {
    formName,
    route,
    multiple = false,
    fieldName,
    filePath,
    formCode,
    pageType,
    mainId,
    fieldTitle,
    accept,
    validExt,
    maxFileSize = -1,
    macroType,
    myActionId,
  } = opt;

  //init open files
  const { files, open, reset, onChange } = useFileDialog({ multiple: multiple, accept: accept });
  open();
  //上传文件处
  onChange((files: any) => {
    console.log('selectFiles files', files);
    let isExtValid = true;
    let isSizeValid = true;
    console.log('maxFileSize', maxFileSize * 1024);

    for (let i = 0; i < files.length; i++) {
      const file = files[i];
      const fileName = file.name;
      const ext = fileName.substring(fileName.lastIndexOf('.') + 1);
      if (validExt && validExt.length > 0) {
        isExtValid = validExt.some((item) => item.toLowerCase() == ext.toLowerCase());
        if (!isExtValid) {
          createMessage.warn('文件: ' + file.name + ' 类型非法');
          return;
        }
      }
      if (maxFileSize != -1) {
        console.log('files.size', file.size);
        if (file.size > maxFileSize * 1024) {
          isSizeValid = false;
          if (!isSizeValid) {
            createMessage.warn('文件: ' + file.name + ' 大小超过了 ' + maxFileSize / 1024 + 'M');
            return;
          }
        }
      }
    }

    // 隐藏上传文件链接，防止网卡时反复点击可以上传
    if (typeof hideUploadLink == 'function') {
      hideUploadLink(formName, fieldName);
    }

    console.log('fieldName', fieldName, 'macroType', macroType);

    // 将图像宏控件的显示迁至了macro_attachment_ctl.js中的displayAttachment方法中，否则删除按钮的位置不便于控制
    // 如果是图像宏控件，则显示所选的图片
    // if (macroType == 'macro_image') {
    //   const curFile = files[0];
    //   console.log('curFile', curFile);
    //   const reader = new FileReader();
    //   reader.readAsDataURL(curFile);
    //   reader.onload = function () {
    //     const w = $(fo(fieldName + 'Img')).attr('w');
    //     const h = $(fo(fieldName + 'Img')).attr('h');
    //     let style = '';
    //     if (w && h) {
    //       style = 'width: ' + w + '; height:' + h;
    //     } else if (w) {
    //       style = 'width: ' + w;
    //     }

    //     $(fo(fieldName + 'Img')).html('<img src="' + reader.result + '" style="' + style + '"/>');
    //   };
    // }

    // const { formName, route } = uploadFileStore.getUploadFileParams;
    uploadFileObjectFunc({
      files,
      formName,
      route,
      fieldName,
      filePath,
      formCode,
      pageType,
      mainId,
      fieldTitle,
      macroType,
      myActionId,
    });
    callback(files);
  });
};

export const uploadFileObjectFunc = (opt = {}) => {
  uploadFileObject({
    ...opt,
  });
};

export const getObjectByKey = async (key: string, fileName) => {
  console.log('getObjectByKey', key, fileName);
  // minio只能通过fileName获取
  if (!key) return;

  // 将key中的//转为/
  key = key.replaceAll('//', '/');
  if (fileName) {
    const params = {
      key,
      fileName,
    };
    const data = await getObsDownloadUrl(params);
    window.open(data.url);
  } else {
    if (userStore.getServerInfo.objStoreType == 'obs') {
      fileObj().getObject(
        key,
        (res): any => {
          console.log('下载结束回调', res);
          const { SignedUrl } = res?.Content;
          window.open(SignedUrl);
        },
        (res) => {
          console.log('下载进度', res);
        },
      );
    }
  }
};

// 显示/隐藏暂停按钮
function showPauseBtn(formName, fieldName, isShow) {
  if (isShow) {
    $('#barBtn_' + formName + '_' + fieldName + '_pause').show();
  } else {
    $('#barBtn_' + formName + '_' + fieldName + '_pause').hide();
  }
}

// 显示/隐藏启动按钮
function showStartBtn(formName, fieldName, isShow) {
  if (isShow) $('#barBtn_' + formName + '_' + fieldName + '_start').show();
  else $('#barBtn_' + formName + '_' + fieldName + '_start').hide();
}

// 找到当前的上传对象
export function getCurUploadObj(formName, fieldName) {
  let curUploadObj;
  const fileTreeList = uploadFileStore.getUploadFileTreeList;
  fileTreeList.forEach((item) => {
    const upObj = item.children.filter(
      (item) => item.formName == formName && item.fieldName == fieldName,
    );
    console.log('getCurUploadObj upObj', upObj);
    curUploadObj = upObj[0];
  });
  return curUploadObj;
}

// 显示/隐藏启动按钮
function showCancelBtn(formName, fieldName, isShow) {
  if (isShow) $('#barBtn_' + formName + '_' + fieldName + '_cancel').show();
  else $('#barBtn_' + formName + '_' + fieldName + '_cancel').hide();
}

function upload(option, uploadFileObj) {
  console.log('upload option', option);

  const spark = new SparkMD5.ArrayBuffer(); // md5的ArrayBuffer加密类
  const fileReader = new FileReader(); // 文件读取类
  const chunkSize = 1024 * 1024 * 30; // 单个分片大小
  const concurrency = 1;
  const chunkContinuesNum = 0;
  const chunkOffset = 0; // 当前分片的偏移值
  let md5 = ''; // 文件的唯一标识
  const optionFile = option.file; // 需要分片的文件
  let fileChunkedList: any = []; // 文件分片完成之后的数组
  const percentage: number[] = []; // 文件上传进度的数组，单项就是一个分片的进度

  console.log('upload option.file', option.file);
  // 首次进来并没有选择文件，因此直接return
  if (option.file === undefined) {
    return;
  }

  // 更新上传进度条百分比
  const updatePercentage = () => {
    let loaded = 0; // 当前已经上传文件的总大小
    percentage.forEach((item) => {
      loaded += item;
    });
    let percent = Math.floor((loaded / optionFile.size) * 100);
    if (percent > 100) {
      percent = 100;
    }

    uploadFileObj.progress = percent;
    const data = uploadFileObj.getOptions();
    finishRefresh(data);
  };

  // 如果已存在chunks，则说明为暂停后续传
  if (uploadFileObj.chunks) {
    for (const k in uploadFileObj.chunks) {
      percentage[k] = uploadFileObj.chunks[k].status === 2 ? chunkSize : 0;
    }
    doUpload(uploadFileObj.chunks, uploadFileObj.md5);
    return;
  }

  // 文件开始分片，push到fileChunkedList数组中
  for (let i = 0; i < optionFile.size; i = i + chunkSize) {
    const tmp = optionFile.slice(i, Math.min(i + chunkSize, optionFile.size));
    console.log('tmp', tmp);
    fileChunkedList.push(tmp);
    // 将每一个分片的数据读取出来
    while (fileReader.readyState !== 1) {
      fileReader.readAsArrayBuffer(tmp);
      break;
    }
  }
  console.log('chunk count', fileChunkedList.length);

  // 创建队列上传任务，concurrency是上传并发数
  function sendRequest(chunks, concurrency = 1, option, md5) {
    showPauseBtn(option.formName, option.fieldName, true);
    showCancelBtn(option.formName, option.fieldName, true);

    let counter = 0;

    return new Promise((resolve: (value?: unknown) => void, reject) => {
      const len = chunks.length;

      // 开始上传
      const start = async () => {
        console.log('chunks', chunks);

        const curUploadObj = getCurUploadObj(option.formName, option.fieldName);
        if (
          curUploadObj &&
          (curUploadObj.status == UploadStatus.PAUSE || curUploadObj.status == UploadStatus.CANCEL)
        ) {
          reject({ isStopped: true });
          return;
        }

        const item = chunks[counter];
        console.log('counter', counter, 'item', item);
        counter++;
        // 如果分片没有上传或续传
        if (item.status === 0 || item.status === 1) {
          item.status = 1;

          console.log('item', item, 'counter', counter);
          // 发送分片
          const res = await uploadChunk(item.formData);
          console.log('res', res);

          if (res.code !== 200) {
            if (curUploadObj.status == UploadStatus.CANCEL) {
              reject(res);
            } else {
              createMessage.error(res.msg);
              curUploadObj.status = UploadStatus.PAUSE;
              reject(res);
            }
          } else {
            console.log('uploadChunk', res.msg);
            item.status = 2;

            uploadFileObj.chunks = chunks;
            uploadFileObj.md5 = md5;
          }

          // 更新进度
          percentage[counter - 1] = chunkSize;
          updatePercentage();

          if (counter - 1 === len - 1) {
            // 最后一个上传完成
            resolve();
          } else {
            start();
          }

          // // 并发上传时判断是否全部完成上传，并发上传时进度条不能及时更新，体验不好
          // let isEnd = true;
          // for (let k in chunks) {
          //   if (chunks[k].status !== 2) {
          //     isEnd = false;
          //   }
          // }
          // if (isEnd) {
          //   resolve();
          // }
        } else {
          // 续传
          start();
        }
      };

      // 如果全部分片并发上传，中间无法暂停，且进度条只能在一开始的时候有更新
      // concurrency = len;
      concurrency = 1;
      while (concurrency > 0) {
        setTimeout(() => {
          start();
        }, Math.random() * 1000);
        concurrency -= 1;
      }
    });
  }

  // 在文件读取完毕之后，开始计算文件md5，作为文件唯一标识
  fileReader.onload = async (e) => {
    spark.append(e.target?.result);
    md5 = spark.end();
    console.log('文件md5', md5);

    // 将fileChunkedList转成FormData对象，并加入上传时需要的数据
    fileChunkedList = fileChunkedList.map((item, index) => {
      const formData = new FormData();
      if (option.data) {
        // 其它参数
        Object.keys(option.data).forEach((key) => {
          formData.append(key, option.data[key]);
        });

        formData.append('chunkFile', item, option.file.name); // 文件
        formData.append('chunkNum', index + 1); // 当前文件块
        formData.append('chunkSize', '' + chunkSize); // 单个分块大小
        if (index + 1 === chunkContinuesNum) {
          formData.append('offset', '' + chunkOffset); // 当前分块偏移值
        }
        formData.append('fileSize', optionFile.size); // 文件总大小
        formData.append('fileMd5', md5); // 文件标识
        formData.append('totalChunks', fileChunkedList.length); // 总块数
      }
      // formData.forEach((value, key) => {
      //   console.log('formData==>', key, value);
      // });
      return { formData: formData, index: index, status: 0 };
    });

    console.log('fileChunkedList', fileChunkedList);

    try {
      // 注册上传文件信息
      const registerData = {
        fileName: option.file.name, // 文件名-合并块时使用
        fileMd5: md5, // 文件MD5值，防止篡改
        fileSize: optionFile.size, // 文件总大小
        chunkSize: chunkSize, // 每个块的大小
        totalChunks: fileChunkedList.length, // 块的总数量
        chunkOffset: 0, // 偏移量
      };

      // 发送文件注册请求
      await registerFile({
        ...registerData,
        ...option.data,
      });

      doUpload(fileChunkedList, md5);
    } catch (error) {
      console.log(error);
      createMessage.error('' + error);
    }
  };

  async function doUpload(fileChunkedList, md5) {
    // 调用上传队列方法 等待所有文件上传完成
    let isStop = false;
    const curUploadObj = getCurUploadObj(option.data.formName, option.data.fieldName);
    curUploadObj.status = UploadStatus.START;
    await sendRequest(fileChunkedList, concurrency, option.data, md5).catch((e) => {
      console.log('sendRequest', e);
      isStop = true;
      if (curUploadObj && curUploadObj.status == UploadStatus.CANCEL) {
        try {
          showUploadLink(option.data.formName, option.data.fieldName, true);
          initAttachmentCtl(option.data.formName, option.data.fieldName, true);
        } catch (e) {
          console.error(e);
        }
      } else {
        showPauseBtn(option.data.formName, option.data.fieldName, false);
        showStartBtn(option.data.formName, option.data.fieldName, true);
        showCancelBtn(option.data.formName, option.data.fieldName, true);
      }
    });
    if (isStop) {
      return;
    }
    const data = {
      fileMd5: md5, // 文件MD5值，防止篡改
      chunkSize: chunkSize, // 每个块的大小
      totalChunks: fileChunkedList.length, // 块的总数量
      ...option.data,
      fileName: option.file.name,
    };
    // 发送文件合并请求
    const res = await mergeFile({
      ...data,
      ...option.data,
    });

    showPauseBtn(option.data.formName, option.data.fieldName, false);
    showStartBtn(option.data.formName, option.data.fieldName, false);
    showCancelBtn(option.data.formName, option.data.fieldName, false);

    console.log('res', res);
    uploadFileObj.progress = 100;
    uploadFileObj.status = UploadStatus.SUCCESS;
    const opts = uploadFileObj.getOptions();
    opts.fieldValue = res.filePath;
    opts.isMerged = true;

    finishRefresh(opts);
  }
}
